﻿using System;

namespace et_Type
{
    public enum biff8_ptg : byte
    {
        NONE = 0xFF,        // 没有此项
        biff8_ptgNop = 0x00,
        biff8_ptgExp = 0x01,	// 数组公式和共享公式
        biff8_ptgTbl = 0x02,	// 模拟运算表
        biff8_ptgAdd = 0x03,	// + 双目加
        biff8_ptgSub = 0x04,	// - 双目减
        biff8_ptgMul = 0x05,	// * 双目乘
        biff8_ptgDiv = 0x06,	// / 双目除
        biff8_ptgPower = 0x07,	// ^ 取幂

        biff8_ptgConcat = 0x08,	// & 连接字符串
        biff8_ptgLT = 0x09,	// <
        biff8_ptgLE = 0x0a,	// <=
        biff8_ptgEQ = 0x0b,	// ==
        biff8_ptgGE = 0x0c,	// >=
        biff8_ptgGT = 0x0d,	// >
        biff8_ptgNE = 0x0e,	// !=
        biff8_ptgIsect = 0x0f,	// 空格 求区域交集

        biff8_ptgUnion = 0x10,	// , Comma sign
        biff8_ptgRange = 0x11,	// : Colon sign
        biff8_ptgUplus = 0x12,	// + 正号
        biff8_ptgUminus = 0x13,	// - 负号
        biff8_ptgPercent = 0x14,	// % 百分号
        biff8_ptgParen = 0x15,	// () 括号
        biff8_ptgMissArg = 0x16,	// 缺省参数，如DCOUNT(Database,,Criteria)
        biff8_ptgStr = 0x17,	// 字符串
        biff8_ptgReserved1 = 0x18,	// 
        biff8_ptgAttr = 0x19,	// 
        biff8_ptgSheet = 0x1a,	// 
        biff8_ptgEndSheet = 0x1b,	// 
        biff8_ptgErr = 0x1c,	// 错误码
        biff8_ptgBool = 0x1d,	// Boolean
        biff8_ptgInt = 0x1e,	// 整型
        biff8_ptgNum = 0x1f,	// 浮点

        // base ptg
        biff8_ptgArray = 0x20,	// 我们的 Matrix 如 {1, 2, 3; 4, 5, 6}
        biff8_ptgFunc = 0x21,	// 函数
        biff8_ptgFuncVar = 0x22,	// 函数变参数
        biff8_ptgName = 0x23,	// 名称
        biff8_ptgRef = 0x24,	// 引用，如	$C$3
        biff8_ptgArea = 0x25,	// 区域，如 $A$1:$B$2
        biff8_ptgMemArea = 0x26,	// 我们的vector, 如 (A1, C3, C3:D5)
        biff8_ptgMemErr = 0x27,	// 
        biff8_ptgMemNoMem = 0x28,	// 
        biff8_ptgMemFunc = 0x29,	// 
        biff8_ptgRefErr = 0x2a,	// 不再存在的引用，如：输入 =A1,    然后删除A列
        biff8_ptgAreaErr = 0x2b,	// 不再存在的区域，如：输入 =A1:B4, 然后删除A:B列 
        biff8_ptgRefN = 0x2c,	// 相对引用, 如	C3
        biff8_ptgAreaN = 0x2d,	// 相对引用, 如 A1:B2
        biff8_ptgMemAreaN = 0x2e,	// 我们的vector相对引用,
        biff8_ptgMemNoMemN = 0x2f,	// 

        biff8_ptgReserved2 = 0x30,	// ?
        biff8_ptgReserved3 = 0x31,	// ?
        biff8_ptgReserved4 = 0x32,	// ?
        biff8_ptgReserved5 = 0x33,	// ?
        biff8_ptgReserved6 = 0x34,	// ?
        biff8_ptgReserved7 = 0x35,	// ?
        biff8_ptgReserved8 = 0x36,	// ?
        biff8_ptgReserved9 = 0x37,	// ?
        biff8_ptgFuncCE = 0x38,	// Macro call; oo 文档1.37, 33页
        biff8_ptgNameX = 0x39,	// External Name, 外部名称
        biff8_ptgRef3d = 0x3a,	// 三维引用
        biff8_ptgArea3d = 0x3b,	// 三维区域
        biff8_ptgRefErr3d = 0x3c,	//	不再存在的三维引用
        biff8_ptgAreaErr3d = 0x3d,	//	不再存在的三维区域

        // Reference Class 20h - 3Fh；	// 返回引用
        biff8_ptgArrayR = 0x20,
        biff8_ptgFuncR = 0x21,
        biff8_ptgFuncVarR = 0x22,
        biff8_ptgNameR = 0x23,
        biff8_ptgRefR = 0x24,
        biff8_ptgAreaR = 0x25,
        biff8_ptgMemAreaR = 0x26,
        biff8_ptgMemErrR = 0x27,
        biff8_ptgMemNoMemR = 0x28,
        biff8_ptgMemFuncR = 0x29,
        biff8_ptgRefErrR = 0x2a,
        biff8_ptgAreaErrR = 0x2b,
        biff8_ptgRefNR = 0x2c,
        biff8_ptgAreaNR = 0x2d,
        biff8_ptgMemAreaNR = 0x2e,
        biff8_ptgMemNoMemNR = 0x2f,

        biff8_ptgFuncCER = 0x38,
        biff8_ptgNameXR = 0x39,
        biff8_ptgRef3dR = 0x3a,
        biff8_ptgArea3dR = 0x3b,
        biff8_ptgRefErr3dR = 0x3c,
        biff8_ptgAreaErr3dR = 0x3d,

        // Value Class 40h - 5Fh；		返回数值
        biff8_ptgArrayV = 0x40,
        biff8_ptgFuncV = 0x41,
        biff8_ptgFuncVarV = 0x42,
        biff8_ptgNameV = 0x43,
        biff8_ptgRefV = 0x44,
        biff8_ptgAreaV = 0x45,
        biff8_ptgMemAreaV = 0x46,
        biff8_ptgMemErrV = 0x47,
        biff8_ptgMemNoMemV = 0x48,
        biff8_ptgMemFuncV = 0x49,
        biff8_ptgRefErrV = 0x4a,
        biff8_ptgAreaErrV = 0x4b,
        biff8_ptgRefNV = 0x4c,
        biff8_ptgAreaNV = 0x4d,
        biff8_ptgMemAreaNV = 0x4e,
        biff8_ptgMemNoMemNV = 0x4f,

        biff8_ptgFuncCEV = 0x58,
        biff8_ptgNameXV = 0x59,
        biff8_ptgRef3dV = 0x5a,
        biff8_ptgArea3dV = 0x5b,
        biff8_ptgRefErr3dV = 0x5c,
        biff8_ptgAreaErr3dV = 0x5d,

        // Array Class 60 - 7Fh；		返回数组
        biff8_ptgArrayA = 0x60,
        biff8_ptgFuncA = 0x61,
        biff8_ptgFuncVarA = 0x62,
        biff8_ptgNameA = 0x63,
        biff8_ptgRefA = 0x64,
        biff8_ptgAreaA = 0x65,
        biff8_ptgMemAreaA = 0x66,
        biff8_ptgMemErrA = 0x67,
        biff8_ptgMemNoMemA = 0x68,
        biff8_ptgMemFuncA = 0x69,
        biff8_ptgRefErrA = 0x6a,
        biff8_ptgAreaErrA = 0x6b,
        biff8_ptgRefNA = 0x6c,
        biff8_ptgAreaNA = 0x6d,
        biff8_ptgMemAreaNA = 0x6e,
        biff8_ptgMemNoMemNA = 0x6f,

        biff8_ptgFuncCEA = 0x78,
        biff8_ptgNameXA = 0x79,
        biff8_ptgRef3dA = 0x7a,
        biff8_ptgArea3dA = 0x7b,
        biff8_ptgRefErr3dA = 0x7c,
        biff8_ptgAreaErr3dA = 0x7d
    }

    public class CPtgHlp
    {
        public static void TracePtgError(byte err, out string sErr)
        {
            switch (err)
            {
                case 0x00: sErr = "#NULL!"; break;
                case 0x07: sErr = "#DIV/0!"; break;
                case 0x0F: sErr = "#VALUE!"; break;
                case 0x17: sErr = "#REF!"; break;
                case 0x1D: sErr = "#NAME!"; break;
                case 0x24: sErr = "#NUM!"; break;
                case 0x2A: sErr = "#N/A!"; break;
                default: sErr = "Unknown"; break;
            }
        }

        public class PTG_INGO
        {
            public biff8_ptg ptg;
            public short ptgSize;   //  负数表示不定长
            public PTG_INGO(biff8_ptg pg, short size)
            {
                ptg = pg;
                ptgSize = size;
            }
        }
        public static readonly PTG_INGO[] biff8_PtgInfos = {
	        new PTG_INGO(biff8_ptg.biff8_ptgNop			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgExp			,	4),
	        new PTG_INGO(biff8_ptg.biff8_ptgTbl			,	4),	// (biff8_ptgTblToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgAdd			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgSub			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgMul			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgDiv			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgPower		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgConcat		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgLT			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgLE			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgEQ			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgGE			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgGT			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgNE			,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgIsect		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgUnion		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgRange		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgUplus		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgUminus		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgPercent		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgParen		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgMissArg		,	0),
	        new PTG_INGO(biff8_ptg.biff8_ptgStr			,  -1),	// 特殊指令: 不定长参数
	        new PTG_INGO(biff8_ptg.biff8_ptgReserved1	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgAttr		,  -1),	// (biff8_ptgAttrToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgSheet		,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgEndSheet	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgErr			,	1),	// BYTE
	        new PTG_INGO(biff8_ptg.biff8_ptgBool		,	1),	// BYTE
	        new PTG_INGO(biff8_ptg.biff8_ptgInt			,	2),	// SHORT (注: 不是WORD)
	        new PTG_INGO(biff8_ptg.biff8_ptgNum			,	8),	// DOUBLE
	        new PTG_INGO(biff8_ptg.biff8_ptgArray		,	7),	// (biff8_ptgArrayToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgFunc	    ,	2),	// (biff8_ptgFuncToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgFuncVar		,	3),	// (biff8_ptgFuncVarToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgName  		,	4),	// (biff8_ptgNameToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgRef			,	4),	// (biff8_ptgRefToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgArea	    ,	8),	// (biff8_ptgAreaToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgMemArea		,	6),	// (biff8_ptgMemAreaToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgMemErr		,	6),	// (biff8_ptgMemErrToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgMemNoMem 	,	6),	// (biff8_ptgMemNoMemToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgMemFunc		,	2),	// (biff8_ptgMemFuncToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgRefErr		,	4),	// (biff8_ptgRefErrToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgAreaErr		,	8),	// (biff8_ptgAreaErrToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgRefN 		,	4),	// (biff8_ptgRefNToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgAreaN		,	8),	// (biff8_ptgAreaNToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgMemAreaN 	,	8),	// (biff8_ptgMemAreaNToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgMemNoMemN	,	6),	// (biff8_ptgMemNoMemNToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgReserved2	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgReserved3	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgReserved4	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgReserved5	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgReserved6	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgReserved7	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgReserved8	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgReserved9	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgFuncCE     	,  -2),
	        new PTG_INGO(biff8_ptg.biff8_ptgNameX		,	6),	// (biff8_ptgNameXToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgRef3d		,	6),	// (biff8_ptgRef3dToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgArea3d		,  10),	// (biff8_ptgArea3dToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgRefErr3d	,	6),	// (biff8_ptgRefErr3dToken)
	        new PTG_INGO(biff8_ptg.biff8_ptgAreaErr3d	,  10)	// (biff8_ptgAreaErr3dToken)
        };
    }

    public class biff8_ptgFuncToken
    {
        public ushort iftab;
        public biff8_ptgFuncToken(byte[] buf, ref int iPos)
        {
            iftab = BitConverter.ToUInt16(buf, iPos);
            iPos += 2;
        }
    }

    public class biff8_ptgFuncVarToken
    {
        protected byte[] dat;
        // 3 个字节
        public biff8_ptgFuncVarToken(byte[] buf, ref int iPos)
        {
            dat = new byte[3];
            Array.Copy(buf, iPos, dat, 0, 3);
            iPos += 3;
        }

        // 0 ~ 6位 参数个数
        public byte GetCargs()
        {
            byte bRet = dat[0];
            bRet &= 0x7F;
            return bRet;
        }

        // 0 ~ 14位 函数ID
        public ushort GetIftab()
        {
            ushort uRet = BitConverter.ToUInt16(dat, 1);
            uRet &= 0x7FFF;
            return uRet;
        }
    }

    public class biff8_ptgExpToken
    {
        public ushort rwFrist;
        public ushort colFirst;
        public biff8_ptgExpToken(byte[] dat, ref int iPos)
        {
            rwFrist = BitConverter.ToUInt16(dat, iPos);
            iPos += 2;
            colFirst = BitConverter.ToUInt16(dat, iPos);
            iPos += 2;
        }
    }

    public class biff8_grbitRef : ICloneable
    {
        public byte col;
        public byte bOp;
        private const byte COL_REL_MASK = 0x40;
        private const byte ROW_REL_MASK = 0x80;
        public biff8_grbitRef(byte _col, bool bRowRel, bool bColRel)
        {
            col = _col;
            bOp = 0;
            if (bRowRel)
            {
                bOp |= ROW_REL_MASK;
            }
            if (bColRel)
            {
                bOp |= COL_REL_MASK;
            }
        }

        public biff8_grbitRef(byte[] dat, ref int iPos)
        {
            col = dat[iPos];
            ++iPos;
            bOp = dat[iPos];
            ++iPos;
        }

        public biff8_grbitRef(byte c, byte op)
        {
            col = c;
            bOp = op;
        }

        public object Clone()
        {
            biff8_grbitRef newObj = new biff8_grbitRef(col, bOp);
            return newObj;
        }

        public bool IsColRelative()
        {
            return (0 != (bOp & COL_REL_MASK));
        }

        public bool IsRowRelative()
        {
            return (0 != (bOp & ROW_REL_MASK));
        }

        public bool Equals(biff8_grbitRef other)
        {
            if (ReferenceEquals(null, other))
            {
                return false;
            }
            if (ReferenceEquals(this, other))
            {
                return true;
            }
            return col.Equals(other.col) && bOp.Equals(other.bOp);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }
            if (ReferenceEquals(this, obj))
            {
                return true;
            }
            if (obj.GetType() != typeof(biff8_grbitRef))
            {
                return false;
            }

            return Equals((biff8_grbitRef)obj);
        }

        public override int GetHashCode()
        {
            return col.GetHashCode() ^ bOp.GetHashCode();
        }
    }

    public class biff8_ptgRefToken : ICloneable
    {
        public ushort row;
        public biff8_grbitRef grbitCol;
        public biff8_ptgRefToken()
        {
        }

        public biff8_ptgRefToken(byte[] dat, ref int iPos)
        {
            row = BitConverter.ToUInt16(dat, iPos);
            iPos += 2;
            grbitCol = new biff8_grbitRef(dat, ref iPos);
        }

        public object Clone()
        {
            biff8_ptgRefToken newObj = new biff8_ptgRefToken();
            newObj.row = row;
            newObj.grbitCol = new biff8_grbitRef(grbitCol.col, grbitCol.bOp);
            return newObj;
        }

        public bool Equals(biff8_ptgRefToken other)
        {
            if (ReferenceEquals(null, other))
            {
                return false;
            }
            if (ReferenceEquals(this, other))
            {
                return true;
            }

            return row.Equals(other.row) && grbitCol.Equals(other.grbitCol);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }
            if (ReferenceEquals(this, obj))
            {
                return true;
            }
            if (obj.GetType() != typeof(biff8_ptgRefToken))
            {
                return false;
            }

            return Equals((biff8_ptgRefToken)obj);
        }

        public override int GetHashCode()
        {
            return row.GetHashCode() ^ grbitCol.GetHashCode();
        }
    }

    public class biff8_ptgRef3dToken
    {
        public ushort ixti;
        public biff8_ptgRefToken refToken;
        public biff8_ptgRef3dToken(byte[] dat, ref int iPos)
        {
            ixti = BitConverter.ToUInt16(dat, iPos);
            iPos += 2;
            refToken = new biff8_ptgRefToken(dat, ref iPos);
        }
    }

    public class biff8_ptgAreaN
    {
        public ushort rwFrist;
        public ushort rwLast;
        private ushort grbitColFirst;    // 14位用来存colFirst，其他两位控制是否相对
        private ushort grbitColLast;
        private const ushort COL_MASK = 0x3FFF;
        public biff8_ptgAreaN(byte[] dat, ref int iPos)
        {
            rwFrist = BitConverter.ToUInt16(dat, iPos);
            rwLast = BitConverter.ToUInt16(dat, iPos + 2);
            grbitColFirst = BitConverter.ToUInt16(dat, iPos + 4);
            grbitColLast = BitConverter.ToUInt16(dat, iPos + 6);
            iPos += 8;
        }

        public ushort GetColFirst()
        {
            return (ushort)(grbitColFirst & COL_MASK);
        }

        public ushort GetColLast()
        {
            return (ushort)(grbitColLast & COL_MASK);
        }

        public biff8_ptgAreaToken ToAreaToken()
        {
            biff8_ptgAreaToken at = new biff8_ptgAreaToken();
            at.rwFirst = rwFrist;
            at.rwTo = rwLast;
            const ushort ROW_REL_MASK = 0x8000;
            const ushort COL_REL_MASK = 0x4000;
            at.grbitColFirst = new biff8_grbitRef((byte)GetColFirst(), 
                (0 != (ROW_REL_MASK & grbitColFirst)), (0 != (COL_REL_MASK & grbitColFirst)));
            at.grbitColTo = new biff8_grbitRef((byte)GetColLast(),
                (0 != (ROW_REL_MASK & grbitColLast)), (0 != (COL_REL_MASK & grbitColLast)));

            return at;
        }
    }

    public class biff8_ptgAreaToken : ICloneable
    {
        public ushort rwFirst;
        public ushort rwTo;
        public biff8_grbitRef grbitColFirst;
        public biff8_grbitRef grbitColTo;
        public biff8_ptgAreaToken()
        {
        }

        public biff8_ptgAreaToken(byte[] dat, ref int iPos)
        {
            rwFirst = BitConverter.ToUInt16(dat, iPos);
            iPos += 2;
            rwTo = BitConverter.ToUInt16(dat, iPos);
            iPos += 2;
            grbitColFirst = new biff8_grbitRef(dat, ref iPos);
            grbitColTo = new biff8_grbitRef(dat, ref iPos);
        }

        public object Clone()
        {
            biff8_ptgAreaToken newObj = new biff8_ptgAreaToken();
            newObj.rwFirst = rwFirst;
            newObj.rwTo = rwTo;
            newObj.grbitColFirst = new biff8_grbitRef(grbitColFirst.col, grbitColFirst.bOp);
            newObj.grbitColTo = new biff8_grbitRef(grbitColTo.col, grbitColTo.bOp);
            return newObj;
        }

        public bool Equals(biff8_ptgAreaToken other)
        {
            if (ReferenceEquals(null, other))
            {
                return false;
            }
            if (ReferenceEquals(this, other))
            {
                return true;
            }
            return rwFirst.Equals(other.rwFirst) && rwTo.Equals(other.rwTo) &&
                grbitColFirst.Equals(other.grbitColFirst) && grbitColTo.Equals(other.grbitColTo);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }
            if (ReferenceEquals(this, obj))
            {
                return true;
            }
            if (obj.GetType() != typeof(biff8_ptgAreaToken))
            {
                return false;
            }
            return Equals((biff8_ptgAreaToken)obj);
        }

        public override int GetHashCode()
        {
            uint uCode = rwFirst;
            uCode <<= 16;
            uCode += rwTo;
            int nGrbitCode = grbitColFirst.GetHashCode() ^ grbitColTo.GetHashCode();
            return uCode.GetHashCode() ^ nGrbitCode;
        }
    }

    public class biff8_ptgArea3dToken
    {
        public short sheet;
        public biff8_ptgAreaToken areaToken;
        public biff8_ptgArea3dToken(byte[] dat, ref int iPos)
        {
            sheet = BitConverter.ToInt16(dat, iPos);
            iPos += 2;
            areaToken = new biff8_ptgAreaToken(dat, ref iPos);
        }
    }

    public class biff8_ptgExtArea3dToken
    {
        public short sheetFrom;
        public short sheetTo;
        public biff8_ptgAreaToken areaToken;
        public biff8_ptgExtArea3dToken(byte[] dat, ref int iPos)
        {
            sheetFrom = BitConverter.ToInt16(dat, iPos);
            iPos += 2;
            sheetTo = BitConverter.ToInt16(dat, iPos);
            iPos += 2;
            areaToken = new biff8_ptgAreaToken(dat, ref iPos);
        }
    }

    public class biff8_ptgNameToken
    {
        public ushort ilbl;
        protected ushort reserved;
        public biff8_ptgNameToken(byte[] dat, ref int iPos)
        {
            ilbl = BitConverter.ToUInt16(dat, iPos);
            iPos += 2;
            reserved = BitConverter.ToUInt16(dat, iPos);
            iPos += 2;
        }
    }

    public class biff8_ptgNameXToken
    {
        public ushort ixti; // Index into the EXTERNSHEET record (0-based)
        public ushort ilbl; // The index to the NAME or EXTERNNAME table (1-based)
        protected ushort reserved;
        public biff8_ptgNameXToken(byte[] dat, ref int iPos)
        {
            ixti = BitConverter.ToUInt16(dat, iPos);
            ilbl = BitConverter.ToUInt16(dat, iPos + 2);
            iPos += 6;
        }
    }

    public class biff8_ptgStrToken
    {
        public byte cch;
        public byte grbitCHR;
        public biff8_ptgStrToken(byte[] dat, ref int iPos)
        {
            cch = dat[iPos];
            ++iPos;
            grbitCHR = dat[iPos];
            ++iPos;
        }
    }

    public class biff8_ptgArrayToken
    {
        public byte ptg;
        public byte cClos;
        public ushort cRows;
        public uint reserved;
        public biff8_ptgArrayToken(byte[] dat, ref int iPos)
        {
            ptg = dat[iPos];
            cClos = dat[iPos + 1];
            cRows = BitConverter.ToUInt16(dat, iPos + 2);
            iPos += 8;
        }
    }

    public class biff8_ptgAttrToken
    {
        protected byte grbits;
        protected ushort reserved;
        public bool IsAttrChoose()
        {
            return 0 != (0x04 & grbits);
        }
        public void Decode(byte[] dat, ref int iPos)
        {
            grbits = dat[iPos];
            reserved = BitConverter.ToUInt16(dat, iPos + 1);
            int nLen = 3;
            if(IsAttrChoose())
            {
                int nSz = (reserved + 1) * 2;
                nLen += nSz;
            }
            iPos += nLen;
        }
    }
}